/*
 * Copyright (c) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import Log from './log.js';
import {mergeModifiers, ProfileHelpers} from './profile_helpers.js'

export class MixPanelPeople extends ProfileHelpers() {
    constructor(mpInstance) {
        super();
        this.mixpanel = mpInstance;
        this.endpoint = '/engage';
    }

    /**
     * people.setOnce(distinctId, prop, to, modifiers, callback)
     * ---
     * The same as people.set but in the words of mixpanel:
     * mixpanel.people.setOnce
     *
     * " This method allows you to set a user attribute, only if
     *     it is not currently set. It can be called multiple times
     *     safely, so is perfect for storing things like the first date
     *     you saw a user, or the referrer that brought them to your
     *     website for the first time. "
     */
    setOnce(distinctId, prop, to, modifiers, callback) {
        Log.showInfo('MixPanelPeople setOnce');
        const identifiers = { $distinctId: distinctId };
        this._set(prop, to, modifiers, callback, { identifiers, setOnce: true });
    }

    /**
     * people.set(distinctId, prop, to, modifiers, callback)
     * ---
     * set properties on an user record in engage
     * usage:
     *     mixpanel.people.set('bob', 'gender', 'm');
     *     mixpanel.people.set('joe', {
     *         'company': 'acme',
     *         'plan': 'premium'
     *     });
     */
    set(distinctId, prop, to, modifiers, callback) {
        Log.showInfo('MixPanelPeople set');
        const identifiers = { $distinctId: distinctId };
        this._set(prop, to, modifiers, callback, { identifiers });
    }

    /**
     * people.increment(distinctId, prop, by, modifiers, callback)
     * ---
     * increment/decrement properties on an user record in engage
     *
     * usage:
     *
     *  mixpanel.people.increment('bob', 'page_views', 1);
     *
     *  // or, for convenience, if you're just incrementing a counter by 1, you can
     *  // simply do
     *  mixpanel.people.increment('bob', 'page_views');
     *
     *  // to decrement a counter, pass a negative number
     *  mixpanel.people.increment('bob', 'credits_left', -1);
     *
     *  // like mixpanel.people.set(), you can increment multiple properties at once:
     *  mixpanel.people.increment('bob', {
     *      counter1: 1,
     *      counter2: 3,
     *      counter3: -2
     *  });
     */
    increment(distinctId, prop, by, modifiers, callback) {
        Log.showInfo('MixPanelPeople increment');
        var $add = {};

        if (typeof (prop) === 'object') {
            if (typeof (by) === 'object') {
                callback = modifiers;
                modifiers = by;
            } else {
                callback = by;
            }

            for (const key in prop) {
                if (Object.prototype.hasOwnProperty.call(prop, key)) {
                    const val = prop[key];
                    const valf = parseFloat(val);
                    if (isNaN(valf) && this.mixpanel.config.debug) {
                        Log.showError('Invalid increment value passed to mixpanel.people.increment - must be a number');
                        Log.showError('Passed ' + key + ':' + val);
                    } else if (!isNaN(valf)) {
                        $add[key] = val;
                    }
                }
            }
        } else {
            if (typeof (by) === 'number' || !by) {
                by = by || 1;
                $add[prop] = by;
                if (typeof (modifiers) === 'function') {
                    callback = modifiers;
                }
            } else if (typeof (by) === 'function') {
                callback = by;
                $add[prop] = 1;
            } else {
                callback = modifiers;
                modifiers = (typeof (by) === 'object') ? by : {};
                $add[prop] = 1;
            }
        }

        var data = {
            '$add': $add,
            '$token': this.mixpanel.token,
            '$distinctId': distinctId
        };

        data = mergeModifiers(data, modifiers);

        if (this.mixpanel.config.debug) {
            Log.showInfo('Sending the following data to Mixpanel (Engage):');
            Log.showInfo(data);
        }

        this.mixpanel.sendRequest({ method: 'GET', endpoint: '/engage', data: data }, callback);
    }

    /**
     * people.append(distinctId, prop, value, modifiers, callback)
     * Append a value to a list-valued people analytics property.
     * usage:
     *    // append a value to a list, creating it if needed
     *    mixpanel.people.append('bob', 'pages_visited', 'homepage');
     *    // like mixpanel.people.set(), you can append multiple properties at once:
     *    mixpanel.people.append('bob', {
     *        list1: 'bob',
     *        list2: 123
     *    });
     */
    append(distinctId, prop, value, modifiers, callback) {
        Log.showInfo('MixPanelPeople append');
        var $append = {};

        if (typeof (prop) === 'object') {
            if (typeof (value) === 'object') {
                callback = modifiers;
                modifiers = value;
            } else {
                callback = value;
            }
            Object.keys(prop).forEach(function (key) {
                $append[key] = prop[key];
            });
        } else {
            $append[prop] = value;
            if (typeof (modifiers) === 'function') {
                callback = modifiers;
            }
        }

        var data = {
            '$append': $append,
            '$token': this.mixpanel.token,
            '$distinctId': distinctId
        };

        data = mergeModifiers(data, modifiers);

        if (this.mixpanel.config.debug) {
            Log.showInfo('Sending the following data to Mixpanel (Engage):');
            Log.showInfo(data);
        }

        this.mixpanel.sendRequest({ method: 'GET', endpoint: '/engage', data: data }, callback);
    }

    /**
     * people.trackCharge(distinctId, amount, properties, modifiers, callback)
     *    ---
     *    Record that you have charged the current user a certain
     *    amount of money.
     *    usage:
     *        // charge a user $29.99
     *        mixpanel.people.trackCharge('bob', 29.99);
     *        // charge a user $19 on the 1st of february
     *        mixpanel.people.trackCharge('bob', 19, { '$time': new Date('feb 1 2012') });
     */
    trackCharge(distinctId, amount, properties, modifiers, callback) {
        Log.showInfo('MixPanelPeople trackCharge');
        if (typeof (properties) === 'function' || !properties) {
            callback = properties || function () {
            };
            properties = {};
        } else {
            if (typeof (modifiers) === 'function' || !modifiers) {
                callback = modifiers || function () {
                };
                if (properties.$ignoreTime || Object.prototype.hasOwnProperty.call(properties, '$ip')) {
                    modifiers = {};
                    Object.keys(properties).forEach(function (key) {
                        modifiers[key] = properties[key];
                        delete properties[key];
                    });
                }
            }
        }

        if (typeof (amount) !== 'number') {
            amount = parseFloat(amount);
            if (isNaN(amount)) {
                Log.showError('Invalid value passed to mixpanel.people.trackCharge - must be a number');
                return;
            }
        }

        properties.$amount = amount;

        if (Object.prototype.hasOwnProperty.call(properties, '$time')) {
            var time = properties.$time;
            if (Object.prototype.toString.call(time) === '[object Date]') {
                properties.$time = time.toISOString();
            }
        }

        var data = {
            '$append': { '$transactions': properties },
            '$token': this.mixpanel.token,
            '$distinctId': distinctId
        };

        data = mergeModifiers(data, modifiers);

        if (this.mixpanel.config.debug) {
            Log.showInfo('Sending the following data to Mixpanel (Engage):');
            Log.showInfo(data);
        }

        this.mixpanel.sendRequest({ method: 'GET', endpoint: '/engage', data: data }, callback);
    }

    /**
     * people.clearCharges(distinctId, modifiers, callback)
     * ---
     * Clear all the current user's transactions.
     * usage:
     *      mixpanel.people.clearCharges('bob');
     */
    clearCharges(distinctId, modifiers, callback) {
        Log.showInfo('MixPanelPeople clearCharges');
        var data = {
            '$set': { '$transactions': [] },
            '$token': this.mixpanel.token,
            '$distinctId': distinctId
        };

        if (typeof (modifiers) === 'function') {
            callback = modifiers;
        }

        data = mergeModifiers(data, modifiers);

        if (this.mixpanel.config.debug) {
            Log.showInfo("Clearing this user's charges:", distinctId);
        }
        this.mixpanel.sendRequest({ method: 'GET', endpoint: '/engage', data: data }, callback);
    }

    /**
     * people.deleteUser(distinctId, modifiers, callback)
     * ---
     * delete an user record in engage
     * usage:
     *     mixpanel.people.deleteUser('bob');
     */
    deleteUser(distinctId, modifiers, callback) {
        Log.showInfo('MixPanelPeople deleteUser');
        const identifiers = { $distinctId: distinctId };
        this._deleteProfile({ identifiers, modifiers, callback });
    }

    /**
     * people.remove(distinctId, data, modifiers, callback)
     * ---
     * remove a value from a list-valued user profile property.
     * usage:
     *     mixpanel.people.remove('bob', {'browsers': 'firefox'});
     *     mixpanel.people.remove('bob', {'browsers': 'chrome', 'os': 'linux'});
     */
    remove(distinctId, data, modifiers, callback) {
        Log.showInfo('MixPanelPeople remove');
        const identifiers = { '$distinctId': distinctId };
        this._remove({ identifiers, data, modifiers, callback })
    }

    /**
     * people.union(distinctId, data, modifiers, callback)
     * ---
     * merge value(s) into a list-valued people analytics property.
     * usage:
     *     mixpanel.people.union('bob', {'browsers': 'firefox'});
     *     mixpanel.people.union('bob', {'browsers': ['chrome'], os: ['linux']});
     */
    union(distinctId, data, modifiers, callback) {
        Log.showInfo('MixPanelPeople union');
        const identifiers = { $distinctId: distinctId };
        this._union({ identifiers, data, modifiers, callback });
    }

    /**
     * people.unset(distinctId, prop, modifiers, callback)
     * ---
     * delete a property on an user record in engage
     * usage:
     *     mixpanel.people.unset('bob', 'page_views');
     *     mixpanel.people.unset('bob', ['page_views', 'last_login']);
     */
    unset(distinctId, prop, modifiers, callback) {
        Log.showInfo('MixPanelPeople unset');
        const identifiers = { $distinctId: distinctId };
        this._unset({ identifiers, prop, modifiers, callback });
    }
}
